﻿using LogLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SoftKeyBoard
{
    public class IDCardCheck
    {
        /// <summary>
        /// 验证身份证号码
        /// </summary>
        /// <param name="Id">身份证号码</param>
        /// <returns>验证成功为True，否则为False</returns>
        public static bool Check(string Id)
        {
            if (Id.Length == 18)
            {
                return Check18(Id);
            }
            else if (Id.Length == 15)
            {
                return Check15(Id);
            }
            else
            {
                return false;
            }
        }
        public static bool Check18(string Id)
        {
            string[] address = { "11", "22", "35", "44", "53", "12", "23", "36", "45", "54", "13", "31", "37", "46", "61", "14", "32", "41", "50", "62", "15", "33", "42", "51", "63", "21", "34", "43", "52", "64", "65", "71", "81", "82", "91" };
            int i = 0;
            for (i = 0; i <= 34; i++)
            {
                if (!(Id.Substring(0, 2) != address[i]))
                {
                    break;
                }
            }
            if (i == 34)
            {
                return false;//省份验证错误
            }
            string birth = Id.Substring(6, 8);
            string year = birth.Substring(0, 4);
            string month = birth.Substring(4, 2);
            string day = birth.Substring(6, 2);
            if (!CheckDate(year, month, day))
            {
                return false;//生日验证错误
            }
            if (!VarifyCode(Id))
            {
                return false;//最后一位校验错误
            }
            return true;
        }

        public static bool IsLeapYear(int year)
        {
            return ((year % 100 != 0 && year % 4 == 0)
                || (year % 100 == 0 && year % 400 == 0));
        }

        public static bool CheckDate(string Year, string Month, string Day)
        {
            try
            {
                int iYear = int.Parse(Year);
                int iMonth = int.Parse(Month);
                int iDay = int.Parse(Day);
                int[] Days = new int[12] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
                if (iMonth < 1 || iMonth > 12)
                {
                    return false;
                }
                Days[1] = IsLeapYear(iYear) ? 29 : 28;
                if (iDay < 0 || iDay > Days[iMonth - 1])
                {
                    return false;
                }
                return true;
            }
            catch (Exception ex)
            {
                Log.WriteException(ex.ToString());
                return false;
            }
        }

        public static bool Check15(string Id)
        {
            return Check18((Id));
        }

        /// <summary>
        /// 获取出生年月
        /// </summary>
        /// <param name="Id">身份证号码</param>
        /// <returns></returns>
        public static string GetBirthday(string Id)
        {
            if (!Check(Id))
            {
                return "";//生日验证错误
            }
            string birth;
            if (Id.Length == 15)
            {
                string newID = Per15To18(Id);
                birth = newID.Substring(6, 8);
                return birth;
            }
            else
            {
                return Id.Substring(6, 8);
            }
        }

        /// <summary>
        /// 获取性别
        /// </summary>
        /// <param name="Id">身份证号码</param>
        /// <returns></returns>
        public static string GetSex(string Id)
        {
            string strID = Id.Substring(Id.Length - 2, 1);
            try
            {
                int nID = int.Parse(strID);
                if (nID % 2 == 0)
                {
                    return "女";
                }
                else
                {
                    return "男";
                }
            }
            catch (Exception ex)
            {
                Log.WriteException(ex.ToString());
                return "";
            }
        }

        public static bool VarifyCode(string Id)
        {
            char[] perIDSrc = Id.ToCharArray();
            int iS = 0;
            int[] iW = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
            char[] LastCode = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };
            for (int i = 0; i < 17; i++)
            {
                iS += (int)(perIDSrc[i] - '0') * iW[i];
            }
            int iY = iS % 11;
            char ch = LastCode[iY];
            return (ch == perIDSrc[perIDSrc.Length - 1]);
        }

        /// <summary>
        /// 实现身份证的15位转18位
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        private static string Per15To18(string Id)
        {
            if (Id.Length != 15)
            {
                return "";
            }
            char[] perIDSrc = Id.ToCharArray();
            int iS = 0;

            //加权因子常数
            int[] iW = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
            //校验码常数
            char[] LastCode = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };

            //新身份证号
            char[] perIDNew = new char[18];
            for (int i = 0; i < 6; i++)
            {
                perIDNew[i] = perIDSrc[i];
            }
            //填在第6位及第7位上填上‘1’，‘9’两个数字
            perIDNew[6] = '1';
            perIDNew[7] = '9';

            for (int i = 8; i < 17; i++)
            {
                perIDNew[i] = perIDSrc[i - 2];
            }

            //进行加权求和
            for (int i = 0; i < 17; i++)
            {
                iS += (perIDNew[i] - '0') * iW[i];
                /*
                            162        对于perIDNew[i]-'0'解释一下：
                            163            perIDNew[i]->ASCII码，取得它的值实际是十进制数；
                            164            '0' ->ASCII码，同上；
                            165            perIDNew[i]-'0' -> 得到具体的十进制数值；
                            166            对于这里面的为什么会进行转换，具体去看C++PRIMER，呵呵。
                            167          */
            }
            //取模运算，得到模值
            int iY = iS % 11;
            //从LastCode中取得以模为索引号的值，加到身份证的最后一位，即为新身份证号。
            perIDNew[17] = LastCode[iY];
            return new string(perIDNew);
        }

        public static int CalculateAge(string id)
        {
            string birthdateString = GetBirthday(id);
            if (string.IsNullOrEmpty(birthdateString))
            {
                return -99;
            }
            try
            {
                DateTime birthdate = DateTime.ParseExact(
                    birthdateString,
                    "yyyyMMdd",
                    System.Globalization.CultureInfo.InvariantCulture);
                DateTime now = DateTime.Now;
                int age = now.Year - birthdate.Year;
                if (now.Month < birthdate.Month
                    || (now.Month == birthdate.Month && now.Day < birthdate.Day))
                {
                    age--;
                }
                return age < 0 ? 0 : age;
            }
            catch
            {
                return -99;
            }
        }
    }
}
